package extensions;

import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import com.imagotag.esl.api.UpdateStatus;
import com.imagotag.esl.api.advanced.LabelData;
import com.imagotag.test.NamedTestCase;
import com.imagotag.test.Store;
import com.imagotag.test.TestCase;
import com.imagotag.test.TestCaseResult;
import com.imagotag.test.enums.LabelConfig;
import com.imagotag.test.enums.LabelCounter;
import com.imagotag.test.enums.LabelSensor;
import com.imagotag.test.results.CsvResultWriter;
import com.imagotag.test.results.TransactionResultInterface;
import com.imagotag.test.results.configs.LabelConfigConverter;
import com.imagotag.test.results.sensors.AutoSensor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * @projectName: esl-test-frame
 * @className: CSVResultWriterWithEV
 * @description:
 * @author: JLONG
 * @date: 2024/8/12 13:54
 * @version: 1.0
 */
@Slf4j
public class CSVResultWriterWithEV extends CsvResultWriter {
    private static final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("dd.MM.yyyy HH:mm:ss");
    private final AutoSensor autoSensor = new AutoSensor();
    public CSVResultWriterWithEV(PrintStream outputStream) {
        super(outputStream);
    }

    private String nullToEmpty(Integer value) {
        return value != null ? Integer.toString(value) : "";
    }

    private String getAutoSensorValue(Integer value) {
        return value != null ? this.autoSensor.getValue(value).toString() : "";
    }

    private String getConvertedLabelConfigValue(LabelConfig labelConfig, Integer value) {
        return value != null ? LabelConfigConverter.convert(labelConfig, value) : "";
    }

    @Override
    public void onTestCaseFinished() {
        TestCase<? extends TestCaseResult> testCase = namedTestCase.getTestCase();
        if (testCase instanceof TestCaseWithEV) {
            // 反射调用TestBuilder的getTestInfo方法获得测试相关信息，如果出现效率低的情况可以抄一下源码
            List<String> values = Lists.newArrayList("FINISHED_TEST_CASE");
            try {
                Method getTestInfo = CsvResultWriter.class.getDeclaredMethod("getTestInfo");
                getTestInfo.setAccessible(true);
                values.addAll((List<String>) getTestInfo.invoke(this));
            } catch (Exception e) {
                log.error("fail to get test info: " + e.getMessage());
            }
            // 如果TestCaseResult有信息，打出来
            TestCaseResult result = ((TestCaseWithEV<?>) testCase).getTestResult();
            Iterable<String> messages = result.getMessages();
            if (messages != null) messages.forEach(values::add);
            this.println(values);
            this.getOutputStream().flush();
        } else {
            super.onTestCaseFinished();
        }
    }

    @Override
    protected List<String> getUpdateStatus(Store store, UpdateStatus updateStatus, TransactionResultInterface transactionResult, int iteration, NamedTestCase namedTestCase) {
        log.info(String.format("updateStatus = %s%n", updateStatus));
        List<String> values = Lists.newArrayList("TASK_FINISHED_EVENT",
                Integer.toString(iteration),
                namedTestCase != null ? Integer.toString(namedTestCase.getIndex()) : "",
                this.getTestCaseName(),
                updateStatus.getTaskType() != null ? updateStatus.getTaskType().toString() : "UNKNOWN",
                updateStatus.getLabelId().toString(),
                FORMATTER.print(updateStatus.getCreatedAt()),
                FORMATTER.print(updateStatus.getLastChangedAt()),
                updateStatus.getStatus().toString(),
                Integer.toString(updateStatus.getErrorCode() != null ? updateStatus.getErrorCode() : 0),
                updateStatus.getErrorDescription() != null ? updateStatus.getErrorDescription() : "",
                updateStatus.getTaskPriority().toString(),
                Integer.toString(updateStatus.getRssi() != null ? updateStatus.getRssi() : 0),
                Integer.toString(updateStatus.getLqi() != null ? updateStatus.getLqi() : 0),
                Integer.toString(updateStatus.getTransmissionTime() != null ? updateStatus.getTransmissionTime() : 0),
                Integer.toString(updateStatus.getAccessPointId() != null ? updateStatus.getAccessPointId() : 0));
        List<LabelData> labelDataList = transactionResult.getLabelDataForTask(updateStatus.getId());

        Map<?, ?> expectedResult = null;
        if (namedTestCase != null){

        }
        TestCase testCase = namedTestCase.getTestCase();
        if (testCase instanceof TestCaseWithEV<?>){
            expectedResult = ((TestCaseWithEV<?>) testCase).getExpectedValue();
        }

        switch (updateStatus.getTaskType()){
            case QUERY_COUNTER:
                for(LabelData labelData: labelDataList){
                    LabelCounter labelCounter = LabelCounter.fromId(labelData.getDataId());
                    int counterVal = labelData.getValue();
                    values.add(labelCounter.toString());
                    values.add("mv=" + this.nullToEmpty(counterVal));
                    if (expectedResult != null && expectedResult.containsKey(labelCounter)){
                        Object expectedObj = expectedResult.get(labelCounter);
                        if (expectedObj instanceof Integer){
                            int expectedVal = (int) expectedObj;
                            values.add("ev=" + Integer.toString(expectedVal));
                            values.add(counterVal == expectedVal ? "PASS" : "NG");
                        }
                        if (expectedObj instanceof Range){
                            Range<Integer> expectedRange = (Range<Integer>) expectedObj;
                            values.add("ev=" + expectedRange.toString());
                            values.add(expectedRange.contains(counterVal) ? "PASS" : "NG");
                        }
                    }
                }
                break;
            case QUERY_SENSOR:
                for(LabelData labelData: labelDataList){
                    LabelSensor labelSensor = LabelSensor.fromId(labelData.getDataId());
                    int sensorVal = labelData.getValue();
                    double readableSensorVal = (double) autoSensor.getValue(sensorVal);
                    values.add(labelSensor.toString());
                    values.add("mv1=" + this.nullToEmpty(sensorVal));
                    values.add("mv2=" + this.getAutoSensorValue(sensorVal));
                    if (expectedResult != null && expectedResult.containsKey(labelSensor)){
                        Object expectedObj = expectedResult.get(labelSensor);
                        if (expectedObj instanceof Double){
                            double expectedVal = (double) expectedObj;
                            values.add("ev=" + Double.toString(expectedVal));
                            values.add(readableSensorVal == expectedVal ? "PASS" : "NG");
                        }
                        if (expectedObj instanceof Range){
                            Range<Double> expectedRange = (Range<Double>) expectedObj;
                            values.add("ev=" + expectedRange.toString());
                            values.add(expectedRange.contains(readableSensorVal) ? "PASS" : "NG");
                        }
                    }
                }
                break;
            case GET_CONFIG:
                for(LabelData labelData: labelDataList){
                    LabelConfig labelConfig = LabelConfig.fromId(labelData.getDataId());
                    int confVal = labelData.getValue();
                    String readableConfVal = this.getConvertedLabelConfigValue(labelConfig, labelData.getValue());
                    values.add(labelConfig.toString());
                    values.add("mv1=" + this.nullToEmpty(confVal));
                    values.add("m2=" + readableConfVal);
                    if (expectedResult != null && expectedResult.containsKey(labelConfig)){
                        String expectedVal = (String) expectedResult.get(labelConfig);
                        boolean res = readableConfVal.equals(expectedVal);
                        values.add("ev=" + expectedVal);
                        values.add(res ? "PASS" : "NG");
                    }
                }
                break;
            default:
                break;
        }
        return values;
    }
}
